Skip to content

[code-scanning-fix] Fix workflow-graphql-id-unescaped: eliminate string interpolation in GraphQL mutations via variables#40757

Merged
pelikhan merged 2 commits into
mainfrom
fix/graphql-id-unescaped-627-628-d08e45c37cbf4f35
Jun 22, 2026
Merged

[code-scanning-fix] Fix workflow-graphql-id-unescaped: eliminate string interpolation in GraphQL mutations via variables#40757
pelikhan merged 2 commits into
mainfrom
fix/graphql-id-unescaped-627-628-d08e45c37cbf4f35

Conversation

@github-actions

@github-actions github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Eliminates GraphQL string interpolation vulnerabilities (CWE-89 / workflow-graphql-id-unescaped) in pkg/cli/project_command.go by converting all three gh api graphql call sites from fmt.Sprintf-based query construction to fully parameterized GraphQL variables.

Changes

pkg/cli/project_command.go — modified (high impact, non-breaking)

Three call sites converted from inline string interpolation to GraphQL variables passed via -f flags:

Call site Before After
createProjectV2 mutation fmt.Sprintf with escapeGraphQLString(title) mutation($ownerId: ID!, $title: String!) + -f ownerId=... -f title=...
repository(...) ID query fmt.Sprintf with escapeGraphQLString on both fields query($owner: String!, $name: String!) + -f owner=... -f name=...
linkProjectV2ToRepository mutation fmt.Sprintf with raw ID interpolation mutation($projectId: ID!, $repositoryId: ID!) + -f projectId=... -f repositoryId=...

Commit 3c4b872ac applied escapeGraphQLString() to un-escaped node ID fields as a stopgap. Commit c969477ed replaces the entire interpolation approach with parameterization, eliminating the need for escapeGraphQLString() at these call sites.

Security

  • Fixes: workflow-graphql-id-unescaped code scanning alerts (CWE-89)
  • Root cause removed: query structure is now static; no caller-supplied value can alter it regardless of content

Testing notes

No new tests added. The parameterized calls are functionally equivalent to the previous interpolated calls for all well-formed inputs.

Checklist

  • Non-breaking change (no API or CLI surface change)
  • Security fix (eliminates injection-class vulnerability)
  • Single file modified (pkg/cli/project_command.go)
  • No new dependencies introduced

Generated by PR Description Updater for issue #40757 · 56.1 AIC · ⌖ 6.91 AIC · ⊞ 4.5K ·

Wrap ownerId, projectId, and repositoryId with escapeGraphQLString() in
the createProjectV2 and linkProjectV2ToRepository GraphQL mutations.

Previously, title was consistently escaped but the node ID fields were
not, creating a defense-in-depth gap. While GitHub API node IDs are
opaque and currently safe, consistently applying escapeGraphQLString()
ensures that if the values ever contain special characters the queries
cannot be altered.

Fixes code scanning alerts #627 and #628.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

@pelikhan pelikhan marked this pull request as ready for review June 22, 2026 14:14
Copilot AI review requested due to automatic review settings June 22, 2026 14:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.

This PR addresses code-scanning warnings for potential GraphQL injection by ensuring GitHub node IDs interpolated into GraphQL mutations are escaped consistently.

Changes:

  • Escape ownerId when building the createProjectV2 GraphQL mutation.
  • Escape projectId and repoId when building the linkProjectV2ToRepository GraphQL mutation.
Show a summary per file
File Description
pkg/cli/project_command.go Escapes node ID inputs embedded into GraphQL mutation strings to close a defense-in-depth injection gap.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 1/1 changed files
  • Comments generated: 2

Comment thread pkg/cli/project_command.go Outdated
Comment thread pkg/cli/project_command.go Outdated
}
}
}`, projectId, repoId)
}`, escapeGraphQLString(projectId), escapeGraphQLString(repoId))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the latest commit. Both createProject and linkProjectToRepo (including its repo-ID query) now use static GraphQL query strings with variables passed as separate -f flags to gh api graphql. This eliminates all string interpolation from the query bodies, removing the need for manual escaping in these mutations entirely.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@github-actions

Copy link
Copy Markdown
Contributor Author

Thanks for this security fix from the Code Scanning Fixer workflow! 🔒 The change is surgical and well-reasoned — wrapping ownerId, projectId, and repoId with the existing escapeGraphQLString() helper achieves consistent defense-in-depth across all three GraphQL mutation arguments in pkg/cli/project_command.go.

One thing that would strengthen this PR:

  • Add tests — the fix itself is minimal and correct, but pkg/cli/project_command.go currently has no test coverage for the escaping behavior. Adding a unit test that exercises createProject and linkProjectToRepo with a node ID that contains special characters (e.g. a value with " or \) would verify the escaping is applied and prevent regressions.

If you'd like a hand adding that coverage, you can assign this prompt to your coding agent:

Add unit tests to pkg/cli/project_command_test.go (create it if it doesn't exist) covering the GraphQL escaping behavior in project_command.go.

Specifically, test the following scenarios:
1. `createProject` called with an `ownerId` containing a double-quote character — verify the resulting GraphQL mutation string has it escaped.
2. `linkProjectToRepo` called with a `projectId` and/or `repoId` containing a backslash — verify both are escaped in the resulting mutation.

Use the existing `escapeGraphQLString` helper as the reference for expected output. Mock or stub the `workflow.RunGH` call so no real API calls are made.

Generated by ✅ Contribution Check · 143.1 AIC · ⌖ 19.3 AIC · ⊞ 5.9K ·

…ing interpolation

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [code-scanning-fix] Fix workflow-graphql-id-unescaped: escape node IDs in GraphQL mutations [code-scanning-fix] Fix workflow-graphql-id-unescaped: eliminate string interpolation in GraphQL mutations via variables Jun 22, 2026
Copilot AI requested a review from pelikhan June 22, 2026 14:55
@pelikhan pelikhan merged commit 716a532 into main Jun 22, 2026
36 checks passed
@pelikhan pelikhan deleted the fix/graphql-id-unescaped-627-628-d08e45c37cbf4f35 branch June 22, 2026 17:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants